package br.com.vm.dao.impl;

import java.lang.reflect.ParameterizedType;
import java.util.Collection;
import java.util.List;

import org.hibernate.SessionFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.transaction.annotation.Transactional;

import br.com.vm.dao._GenericDAO;
import br.com.vm.model.base.Base;

/**
 * Implementacao da interface designada para estrutura de arquitetura da camada
 * DAO.
 * 
 * @author Rodrigo
 * @param <T>
 */
public abstract class _GenericDAOImpl<T extends Base> implements _GenericDAO<T> {

	private static final long serialVersionUID = 5008295763939452920L;
	@Autowired
	protected SessionFactory sessionFactory;

	/**
	 * Metodo que realiza o incremente ou a atualizacao de um dado na base de
	 * dados.
	 * 
	 * @param t
	 *            .
	 * @return T.
	 * @throws Exception
	 */
	@Override
	@Transactional
	@SuppressWarnings("unchecked")
	public T salvar(T t) throws Exception {
		if (t != null) {
			if (isPersistido(t)) {
				sessionFactory.getCurrentSession().persist(t);
			} else {
				t = (T) sessionFactory.getCurrentSession().merge(t);
			}
		} else {
			throw new Exception("The object passed can not be null.");
		}
		return t;
	}

	/**
	 * Metodo que realiza o incremente ou a atualizacao de um uma colecao de
	 * dados do mesmo tipo na base de dados.
	 * 
	 * @param list
	 *            .
	 * @throws Exception
	 */
	@Override
	@Transactional
	public void salvarTodos(final Collection<T> list) throws Exception {
		if (list != null) {
			for (final T t : list) {
				this.salvar(t);
			}
		}
	}

	/**
	 * Metodo que realiza a remoção de um dado na base de dados.
	 * 
	 * @param t
	 *            .
	 */
	@Override
	@Transactional
	public void remover(T t) throws Exception {
		t = this.procurar(t);
		sessionFactory.getCurrentSession().delete(t);
	}

	/**
	 * Metodos que realiza a remocao de uma lista na base de dados.
	 * 
	 * @param list
	 *            .
	 * @throws Exception.
	 */
	@Override
	@Transactional
	public void removerTodos(final Collection<T> list) throws Exception {
		for (final T t : list) {
			this.remover(t);
		}
		sessionFactory.getCurrentSession().flush();
	}

	/**
	 * Metodo que resgata todos os registros armazenados na base de dados de uma
	 * entidade em comum.
	 * 
	 * @return Collection<T>.
	 * @throws Exception
	 */
	@Override
	@SuppressWarnings("unchecked")
	public List<T> listarTodos() throws Exception {
		ParameterizedType pt = (ParameterizedType) this.getClass()
				.getGenericSuperclass();
		List<T> result = sessionFactory.getCurrentSession()
				.createCriteria((Class<T>) pt.getActualTypeArguments()[0])
				.list();
		return result;
	}

	/**
	 * Metodo que restaga um registro armazenado na base de dados a partir de
	 * seu ID.
	 * 
	 * @param t
	 * @return T.
	 */
	@Override
	@SuppressWarnings("unchecked")
	public T procurar(final T t) {
		return (T) sessionFactory.getCurrentSession().get(t.getClass(),
				t.getId());
	}

	@Override
	public void refresh(T t) {
		if (sessionFactory.getCurrentSession().contains(t)) {
			sessionFactory.getCurrentSession().refresh(t);
		} else {
			t = this.procurar(t);
		}
	}

	private boolean isPersistido(final Base poBase) {
		return poBase != null && poBase.getId() != null;
	}
}
